﻿using System;
using System.Linq;
using CID.Domain.Development;
using NUnit.Framework;

namespace CID.Execution.Controller.Tests
{

    [TestFixture]
    class DeploymentStateTests : BaseControllerTests
    {
        private static readonly Guid DeploymentUniqueIdentifier = Guid.NewGuid();
        private const int Location1Id = 1;
        private const int Location2Id = 2;
        private IComponent[] Location1Components;
        private IComponent[] Location2Components;
        private IVersion[] Location1Versions;
        private IVersion[] Location2Versions;
        private DeploymentState deploymentState;

        public override void SetUp()
        {
            base.SetUp();
            Location1Components = new IComponent[] { DevelopmentData.Components[0], DevelopmentData.Components[3], DevelopmentData.Components[4] };
            Location2Components = new IComponent[] { DevelopmentData.Components[1], DevelopmentData.Components[2], DevelopmentData.Components[5], DevelopmentData.Components[6] };
            Location1Versions = new IVersion[] { DevelopmentData.Components[0].Versions.Skip(1).Single() };
            Location2Versions = new IVersion[] { DevelopmentData.Components[1].Versions.Skip(1).Single(), DevelopmentData.Components[2].Versions.Skip(1).Single() };
            deploymentState = new DeploymentState(DeploymentUniqueIdentifier, DevelopmentData.ActiveRelease, OperationsData.Environment);
        }

        #region Constructor
        [Test]
        public void TestCreateNew_ShouldGetOneLocationSpecificPerLocation()
        {
            Assert.AreEqual(DeploymentUniqueIdentifier, deploymentState.DeploymentUniqueIdentifier);
            Assert.AreEqual(2, deploymentState.DeactivationScripts.Count());
            Assert.AreEqual(2, deploymentState.DeploymentScripts.Count());
            Assert.AreEqual(2, deploymentState.ActivationScripts.Count());
            Assert.AreEqual(deploymentState.DeactivationScripts.Count(), deploymentState.PendingDeactivations);
            Assert.AreEqual(deploymentState.DeploymentScripts.Count(), deploymentState.PendingDeployments);
            Assert.AreEqual(deploymentState.ActivationScripts.Count(), deploymentState.PendingActivations);
            Assert.AreEqual(0, deploymentState.DeactivationErrors);
            Assert.AreEqual(0, deploymentState.DeploymentErrors);
            Assert.AreEqual(0, deploymentState.ActivationErrors);
            Assert.IsTrue(deploymentState.Successful);

            CollectionAssert.IsSubsetOf(deploymentState.DeactivationScripts.Single(x => x.LocationId == 1).Steps.Select(x => x.Component.Id).ToArray(), Location1Components.Select(x => x.Id).ToArray());
            CollectionAssert.IsSubsetOf(deploymentState.DeactivationScripts.Single(x => x.LocationId == 2).Steps.Select(x => x.Component.Id).ToArray(), Location2Components.Select(x => x.Id).ToArray());
            CollectionAssert.IsSubsetOf(deploymentState.DeploymentScripts.Single(x => x.LocationId == 1).Steps.Select(x => x.Version.Component.Id).ToArray(), Location1Components.Select(x => x.Id).ToArray());
            CollectionAssert.IsSubsetOf(deploymentState.DeploymentScripts.Single(x => x.LocationId == 2).Steps.Select(x => x.Version.Component.Id).ToArray(), Location2Components.Select(x => x.Id).ToArray());
            CollectionAssert.IsSubsetOf(deploymentState.ActivationScripts.Single(x => x.LocationId == 1).Steps.Select(x => x.Component.Id).ToArray(), Location1Components.Select(x => x.Id).ToArray());
            CollectionAssert.IsSubsetOf(deploymentState.ActivationScripts.Single(x => x.LocationId == 2).Steps.Select(x => x.Component.Id).ToArray(), Location2Components.Select(x => x.Id).ToArray());
            CollectionAssert.IsSubsetOf(deploymentState.DeploymentScripts.Single(x => x.LocationId == 1).Steps.Select(x => x.Version.Name).ToArray(), Location1Versions.Select(x => x.Name).ToArray());
            CollectionAssert.IsSubsetOf(deploymentState.DeploymentScripts.Single(x => x.LocationId == 2).Steps.Select(x => x.Version.Name).ToArray(), Location2Versions.Select(x => x.Name).ToArray());
        }
        #endregion

        #region NotifyActivationCompleted
        [Test]
        public void TestNotifyActivationCompleted_WithoutError_ShouldDecreasePendingActivations()
        {
            var originalPendingActivations = deploymentState.PendingActivations;
            var originalActivationErrors = deploymentState.ActivationErrors;

            deploymentState.NotifyActivationCompleted(Location1Id, false);

            Assert.AreEqual(originalPendingActivations - 1, deploymentState.PendingActivations);
            Assert.AreEqual(originalActivationErrors, deploymentState.ActivationErrors);
            Assert.IsTrue(deploymentState.Successful);
            Assert.IsFalse(deploymentState.LocationHasErrors(Location1Id));
            CollectionAssert.IsEmpty(deploymentState.LocationsWithErrors);
        }

        [Test]
        public void TestNotifyActivationCompleted_WithError_ShouldDecreasePendingActivationsAndIncreaseActivationErrors()
        {
            var originalPendingActivations = deploymentState.PendingActivations;
            var originalActivationErrors = deploymentState.ActivationErrors;

            deploymentState.NotifyActivationCompleted(Location1Id, true);

            Assert.AreEqual(originalPendingActivations - 1, deploymentState.PendingActivations);
            Assert.AreEqual(originalActivationErrors + 1, deploymentState.ActivationErrors);
            Assert.IsFalse(deploymentState.Successful);
            Assert.IsTrue(deploymentState.LocationHasErrors(Location1Id));
            CollectionAssert.Contains(deploymentState.LocationsWithErrors, Location1Id);
        }
        #endregion

        #region NotifyDeploymentCompleted
        [Test]
        public void TestNotifyDeploymentCompleted_WithoutError_ShouldDecreasePendingDeployments()
        {
            var originalPendingDeployments = deploymentState.PendingDeployments;
            var originalDeploymentErrors = deploymentState.DeploymentErrors;

            deploymentState.NotifyDeploymentCompleted(Location1Id, false);

            Assert.AreEqual(originalPendingDeployments - 1, deploymentState.PendingDeployments);
            Assert.AreEqual(originalDeploymentErrors, deploymentState.DeploymentErrors);
            Assert.IsTrue(deploymentState.Successful);
            Assert.IsFalse(deploymentState.LocationHasErrors(Location1Id));
            CollectionAssert.IsEmpty(deploymentState.LocationsWithErrors);
        }

        [Test]
        public void TestNotifyDeploymentCompleted_WithError_ShouldDecreasePendingDeploymentsAndIncreaseDeploymentErrors()
        {
            var originalPendingDeployments = deploymentState.PendingDeployments;
            var originalDeploymentErrors = deploymentState.DeploymentErrors;

            deploymentState.NotifyDeploymentCompleted(Location1Id, true);

            Assert.AreEqual(originalPendingDeployments - 1, deploymentState.PendingDeployments);
            Assert.AreEqual(originalDeploymentErrors + 1, deploymentState.DeploymentErrors);
            Assert.IsFalse(deploymentState.Successful);
            Assert.IsTrue(deploymentState.LocationHasErrors(Location1Id));
            CollectionAssert.Contains(deploymentState.LocationsWithErrors, Location1Id);
        }
        #endregion

        #region NotifyDeactivationCompleted
        [Test]
        public void TestNotifyDeactivationCompleted_WithoutError_ShouldDecreasePendingDeactivations()
        {
            var originalPendingDeactivations = deploymentState.PendingDeactivations;
            var originalDeactivationErrors = deploymentState.DeactivationErrors;

            deploymentState.NotifyDeactivationCompleted(Location1Id, false);

            Assert.AreEqual(originalPendingDeactivations - 1, deploymentState.PendingDeactivations);
            Assert.AreEqual(originalDeactivationErrors, deploymentState.DeactivationErrors);
            Assert.IsTrue(deploymentState.Successful);
            Assert.IsFalse(deploymentState.LocationHasErrors(Location1Id));
            CollectionAssert.IsEmpty(deploymentState.LocationsWithErrors);
        }

        [Test]
        public void TestNotifyDeactivationCompleted_WithError_ShouldDecreasePendingDeactivationsAndIncreaseDeactivationErrors()
        {
            var originalPendingDeactivations = deploymentState.PendingDeactivations;
            var originalDeactivationErrors = deploymentState.DeactivationErrors;

            deploymentState.NotifyDeactivationCompleted(Location1Id, true);

            Assert.AreEqual(originalPendingDeactivations - 1, deploymentState.PendingDeactivations);
            Assert.AreEqual(originalDeactivationErrors + 1, deploymentState.DeactivationErrors);
            Assert.IsFalse(deploymentState.Successful);
            Assert.IsTrue(deploymentState.LocationHasErrors(Location1Id));
            CollectionAssert.Contains(deploymentState.LocationsWithErrors, Location1Id);
        }
        #endregion

        #region LocationHasErrors
        [Test]
        public void TestLocationHasErrors_NoErrorsNotified_ShouldReturnFalseForAllLocation()
        {
            Assert.IsFalse(deploymentState.LocationHasErrors(OperationsData.Location1.Id));
            Assert.IsFalse(deploymentState.LocationHasErrors(OperationsData.Location2.Id));
        }

        [Test]
        public void TestLocationHasErrors_WithErrorNotifiedOnOneLocation_ShouldReturnTrueForFailingLocation()
        {
            deploymentState.NotifyDeactivationCompleted(OperationsData.Location1.Id, true);
            
            Assert.IsTrue(deploymentState.LocationHasErrors(OperationsData.Location1.Id));
            Assert.IsFalse(deploymentState.LocationHasErrors(OperationsData.Location2.Id));
        }

        [Test]
        public void TestLocationHasErrors_WithErrorNotifiedOnAllLocation_ShouldReturnTrueForAllLocation()
        {
            deploymentState.NotifyDeactivationCompleted(OperationsData.Location1.Id, true);
            deploymentState.NotifyDeactivationCompleted(OperationsData.Location2.Id, true);

            Assert.IsTrue(deploymentState.LocationHasErrors(OperationsData.Location1.Id));
            Assert.IsTrue(deploymentState.LocationHasErrors(OperationsData.Location2.Id));
        }
        #endregion

        #region General
        [Test]
        public void TestNotifyError_SameLocationDifferentStages_ShouldAddLocationWithErrorOnlyOnce()
        {
            deploymentState.NotifyDeactivationCompleted(Location1Id, true);
            deploymentState.NotifyDeploymentCompleted(Location1Id, true);
            deploymentState.NotifyActivationCompleted(Location1Id, true);

            CollectionAssert.AreEquivalent(new int[] { Location1Id }, deploymentState.LocationsWithErrors);
        }

        [Test]
        public void TestNotifyError_DifferentLocationsSameStage_ShouldAddEachLocationWithError()
        {
            deploymentState.NotifyDeactivationCompleted(Location1Id, true);
            deploymentState.NotifyDeactivationCompleted(Location2Id, true);

            CollectionAssert.AreEquivalent(new int[] { Location1Id, Location2Id }, deploymentState.LocationsWithErrors);
        }
        #endregion
    }
}
